
#include "network_node.h"
#include "crc32.h"

SC_HAS_PROCESS(network_node);

std::vector<packet::mac_address> network_node::m_address_table;
std::map<packet::mac_address, std::string> network_node::m_address_map;

packet::mac_address network_node::next_address()
{
    packet::mac_address new_addr;    
    new_addr = ((rand()) + (static_cast<packet::mac_address>(rand())<<32)) & 
                 packet::m_ADDR_MASK;    
    m_address_table.push_back(new_addr);
    
    return new_addr;            
}

network_node::network_node(const sc_core::sc_module_name& name,
                            packet::mac_address node_addr) 
: sc_core::sc_module(name)
{               
    m_node_addr = node_addr;    
    m_address_map[m_node_addr] = sc_core::sc_module::name();                
    m_read_buf = NULL;    
    m_send_frame = NULL;
    m_transmition_ok = true;

    SC_THREAD(send);
    
    SC_THREAD(repeat);
    
    SC_THREAD(release_token);
    
    SC_THREAD(receive);
    sensitive << in;        
}

network_node::~network_node()
{    
}

// готовы данные для отправки или нет

bool network_node::is_recv_data_ready()
{
    return rand()%3==0;
}

// генерируем случайный фрейм данных

frame *network_node::generate_random_frame(const token& tkn)
{
    frame *fr = new frame;
    
    fr->ac = tkn.ac;
    fr->ac.t = 1;
    
    unsigned char fc_mask = 0x3F;
    unsigned char frame_type = 0;       // MAC-frame
    
    fr->ri.resize(0);                   // маршрутная информация отсутствует
    
    // генерируем случайные данные
    int data_size = rand()%(packet::m_MAX_DATA_SIZE-1)+1;
    fr->data.resize(data_size);
    for (int i=0; i<data_size; ++i)
        fr->data[i] = rand();
    
    // адрес получателя берем из таблицы адресов
    packet::mac_address dest_addr;
    while (true)
    {
        dest_addr = m_address_table[rand()%m_address_table.size()];    
        if (dest_addr != m_node_addr)
            break;
    }
    
    // генерируем случайные данные, 60 байт 
    packet::data_field &data = fr->data;
    data.resize(60);
    for (int i = 0; i < 60; ++i) {
        data[i] = rand()%256;
    }

    fr->da = dest_addr;
    fr->sa = m_node_addr;         
    
    std::cout << sc_core::sc_time_stamp()
              << " : " << sc_core::sc_module::name() 
              << ". Send random frame to " << m_address_map[fr->da]
              << ". " << std::endl;
    
    // контрольная сумма используем CRC32 (как в Ethernet)            
    fr->fcs = crc32(&(fr->data[0]), (fr->data).size());
    // std::cout << " \n ** " << fr->fcs << " ** \n " << std::flush;
    
    return fr;
}

// отправка данных

void network_node::send()
{            
    while (true)
    {       
        // ожидаем токена, что бы захватить его и начать передачу
        sc_core::wait(m_token_seize_evt);       
  
        // генерируем новый случайный пакет, если прошлая передача прошла успешно
        if (m_transmition_ok) {               
            delete m_send_frame;
            m_send_frame = generate_random_frame(*static_cast<token *>(m_read_buf));
        }
        else {
            std::cout << sc_core::sc_time_stamp()
                      << " : " << sc_core::sc_module::name() 
                      << ". Retry send frame to " << m_address_map[m_send_frame->da]                     
                      << ". " << std::endl;
        }
                
        // ожидаем 5 мкс
        sc_core::wait(5.0, sc_core::SC_US);     
        
        // запись в порт
        out->write(*m_send_frame);        
        
        // delete fr;        
    }
}

// прием данных

void network_node::receive()
{
    while (true)
    {                
        // слушаем порт, ожидаем приема данных
        sc_core::wait();                        
                        

        std::cout << sc_core::sc_time_stamp()  << " : " 
                  << sc_core::sc_module::name() 
                  << ". Receive" << std::flush;                
                
        // читаем данные из порта
        if (!m_read_buf)
            delete m_read_buf;
                    
        m_read_buf = in->read(); 
                
        // проверяем типы пакетов
        
        // принят токен
        if (token *tk = dynamic_cast<token *>(m_read_buf))
        {
            std::cout << " token. " << std::flush;
            
            // захват токена, если есть данные, то переходим к отправке
            if (is_recv_data_ready())
            {
                std::cout << "Token hold, ready for transmit. " << std::endl;
                m_token_seize_evt.notify(sc_core::SC_ZERO_TIME);                 
            }            
            // нет данных для передачи, пересылаем пакет дальше
            else
            {
                std::cout << "No data for transmit. " << std::flush;
                m_packet_repeat_evt.notify(5, sc_core::SC_US);
            }                            
        }
        
        // принят фрэйм
        else if (frame *fr = dynamic_cast<frame *>(m_read_buf))
        {            
            std::cout << " frame. " << std::flush;
            
            // проверка адреса назначение
            
            // данные предназначены узлу
            if (fr->da == m_node_addr)         
            {
                // принимаем данные
                std::cout << "Recognized dest addr. " << std::flush;
                
                // устанавливаем бит того, что адрес распознан
                fr->fs = fr->fs | packet::m_FS_A_MASK;

                // проверка на ошибки
                packet::frame_check_seq crc = crc32(&(fr->data[0]), (fr->data).size());
                
                // std::cout << " \n ** " << crc << " ** \n " << std::flush;
                if (fr->fcs == crc) {
                    
                    // если ошибок нет, то устанавливаем бит того, что фрейм скопирован                    
                    fr->fs = fr->fs | packet::m_FS_C_MASK;
                    std::cout << "Copy data. " << std::flush;
                }
                else {
                    std::cout << "CRC error! " << std::flush;
                }
                                
                // пересылка данных следующему узлу
                m_packet_repeat_evt.notify(5, sc_core::SC_US);            
            }                   
            
            // вернулся отправленный фрейм
            else if (fr->sa == m_node_addr)          
            {
                std::cout << "Recognized source addr. " << std::flush;
                
                // проверка на ошибки, на то что фрейм скопирован
                m_transmition_ok = fr->fs & packet::m_FS_C_MASK;
                if (!m_transmition_ok) {
                    std::cout << "Data not copied! Will retry at next transmition. " << std::flush;
                }
                
                // передаем дальше токен
                m_token_release_evt.notify(5, sc_core::SC_US);
            }
            
            // отправляем пакет дальше
            else 
            {
                m_packet_repeat_evt.notify(5, sc_core::SC_US);
            }
                                                  
        }
        // принята прерывающая последовательность        
        else if (abort_seq *test = dynamic_cast<abort_seq *>(m_read_buf))
        {
            std::cout << " abort sequence." << std::endl;
        }
        // неизвестный тип пакета        
        else
        {
            std::cout << " unknown packet type." << std::endl;
        }                       
    }
}

// передаем данные следующему узлу

void network_node::repeat()
{
    while (true)
    {
        // ожидаем события
        sc_core::wait(m_packet_repeat_evt);
        std::cout << " Send packet to next node. " << std::endl;
        out->write(*m_read_buf);        
    }
}

// освобождаем токен, передаем его следующему узлу

void network_node::release_token()
{
    while (true)
    {        
        sc_core::wait(m_token_release_evt);
        
        std::cout << "Release token. Send token to next node. " << std::endl;
        
        // генерируем первый токен, без поля приоритетов и резервирования
        token *t = new token(0, false, 0);        
        out->write(*t);
        delete t;
    }
}
